﻿using UnityEngine;
using System.Collections;

public class CollisionDetection : MonoBehaviour {
	public static CollisionDetection Instance;
	public float xDist{
		private get{return _xDist;} 
		set{ 
			_xDist = value;
			_xVector = new Vector3(_xDist, 0, 0);
			_halfXVector = new Vector3(_xDist/2, 0, 0);
		}
	}
	public float yDist{
		private get{return _yDist;}  
		set{
			_yDist = value;
			_yVector = new Vector3(0, _yDist, 0);
			_halfYVector = new Vector3(0, _yDist / 2, 0);
		}
	}
	public float rayDist{
		private get{return _rayDist;}  
		set{_rayDist = value;}
	}
	private Vector3 _originPoint, _auxiliaryPoint, _targetPoint;
	private Vector3 _xVector, _halfXVector, _yVector, _halfYVector;
	private float _xDist, _yDist, _rayDist;
	private Vector3 _prePos;
//	private Vector3 _leftUpPnt, _leftMidPnt, _leftDownPnt, _rightUpPnt, _rightDownPnt, _rightMidPnt, _upMidPnt, _downMidPnt;
	private Vector3 _leftPnt, _rightPnt, _upPnt, _downPnt;
	private Vector3 _adjLeftPnt, _adjRightPnt, _adjUpPnt, _adjDownPnt;
	private Vector3 _yOffset, _xOffset;
	private LayerMask _worldMask;
	private bool _isClockwise;
	void Awake () {
		Instance = this;
		_worldMask = 1<<9;
		_yOffset = new Vector3(0,0.05f, 0);
		_xOffset = new Vector3(0.05f, 0, 0);
	}
	
	void Start(){
		
	}
	
	void Update () {
	
	}
	
	public bool IsGoingToTurn(Vector3 moveDirct, CollisionDirection colDirct){
		if(moveDirct == Vector3.right && colDirct == CollisionDirection.Down){
			if((! Physics.Raycast(_rightPnt,Vector3.down,  _rayDist + _yDist, _worldMask))
				&& (Physics.Raycast(_leftPnt, Vector3.down,  _rayDist + _yDist, _worldMask))
				&& !IsCollidingRight()){
					return true;
			}
		}else if(moveDirct == Vector3.left && colDirct == CollisionDirection.Down){
			if( (!Physics.Raycast(_leftPnt, Vector3.down, _rayDist + _yDist, _worldMask))
				&& (Physics.Raycast(_rightPnt, Vector3.down,  _rayDist + _yDist, _worldMask))
				&& !IsCollidingLeft()){
					return true;
			}
		}else if(moveDirct == Vector3.right && colDirct == CollisionDirection.Up){
			if( (!Physics.Raycast(_rightPnt, Vector3.up,  _rayDist + _yDist, _worldMask))
				&& (Physics.Raycast(_leftPnt, Vector3.up,  _rayDist + _yDist, _worldMask))
				&& !IsCollidingRight()){
					return true;
			}
		}else if(moveDirct == Vector3.left && colDirct == CollisionDirection.Up){
			if( (!Physics.Raycast(_leftPnt, Vector3.up,  _rayDist + _yDist, _worldMask))
				&& (Physics.Raycast(_rightPnt, Vector3.up,  _rayDist + _yDist, _worldMask))
				&& !IsCollidingLeft()){
					return true;
			}
		}else if(moveDirct == Vector3.up && colDirct == CollisionDirection.Left){
			if( (!Physics.Raycast(_upPnt, Vector3.left, _rayDist + _xDist, _worldMask))
				&& (Physics.Raycast(_downPnt, Vector3.left,  _rayDist + _xDist, _worldMask))
				&& !IsCollidingUp()){
					return true;
			}
		}else if(moveDirct == Vector3.down && colDirct == CollisionDirection.Left){
			
			if( (!Physics.Raycast(_downPnt, Vector3.left,  _rayDist + _xDist, _worldMask))
				&& (Physics.Raycast(_upPnt, Vector3.left, _rayDist + _xDist, _worldMask))
				&& !IsCollidingDown()){
					return true;
			}
		}else if(moveDirct == Vector3.up && colDirct == CollisionDirection.Right){
			if( (!Physics.Raycast(_upPnt, Vector3.right,  _rayDist + _xDist, _worldMask))
				&& (Physics.Raycast(_downPnt, Vector3.right,  _rayDist + _xDist, _worldMask))
				&& !IsCollidingUp()){
					return true;
			}
		}else if(moveDirct == Vector3.down && colDirct == CollisionDirection.Right){
			if( (!Physics.Raycast(_downPnt, Vector3.right,  _rayDist + _xDist, _worldMask))
				&& (Physics.Raycast(_upPnt, Vector3.right,  _rayDist + _xDist, _worldMask))
				&& !IsCollidingDown()){
					return true;
			}
		}
		
		return false;
	}
	
	//being called when current collision direction is none
	//return the moveinfo
	public MoveInfo GetTurningDirection(Vector3 pos, Vector3 moveDirct, CollisionDirection colDirct){
		RaycastHit hit1, hit2;
		MoveInfo moveInfo;
		_prePos = pos;
		moveInfo.isTurning = true;
		if(moveDirct == Vector3.right && colDirct == CollisionDirection.Down){
			
//			Debug.DrawRay(pos + _halfXVector, Vector3.down, Color.red);
//			Debug.Log(Physics.Raycast(pos + _halfXVector,Vector3.down,  _rayDist + _yDist/2) +","+ Physics.Raycast(pos - _halfXVector, Vector3.down,  _rayDist + _yDist/2));
//			Debug.DrawRay(pos - _halfXVector, Vector3.down, Color.green);
//			Debug.LogError("There!!!");
			if((! Physics.Raycast(pos + _halfXVector,Vector3.down,  _rayDist + _yDist/2, _worldMask))
				&& (Physics.Raycast(pos - _halfXVector, Vector3.down,  _rayDist + _yDist/2, _worldMask))){
				//	Debug.Log("here!");
					_originPoint = pos;
					_auxiliaryPoint = pos + _halfXVector;
					_targetPoint = pos + _halfXVector - _halfYVector - _yOffset;
					moveInfo.moveDirection = Vector3.down;
					moveInfo.VerticalDirection = Vector3.right;
					_isClockwise = true;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.left && colDirct == CollisionDirection.Down){
			if( (!Physics.Raycast(pos - _halfXVector, Vector3.down, _rayDist + _yDist / 2, _worldMask))
				&& (Physics.Raycast(pos +_halfXVector, Vector3.down,  _rayDist + _yDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos - _halfXVector;
					_targetPoint = pos - _halfXVector - _halfYVector - _yOffset;
					moveInfo.moveDirection = Vector3.down;
					moveInfo.VerticalDirection = Vector3.left;
					_isClockwise = false;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.right && colDirct == CollisionDirection.Up){
			if( (!Physics.Raycast(pos + _halfXVector, Vector3.up,  _rayDist + _yDist / 2, _worldMask))
				&& (Physics.Raycast(pos -_halfXVector, Vector3.up,  _rayDist + _yDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos + _halfXVector;
					_targetPoint = pos + _halfXVector + _halfYVector + _yOffset;
					moveInfo.moveDirection = Vector3.up;
					moveInfo.VerticalDirection = Vector3.right;
					_isClockwise = false;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.left && colDirct == CollisionDirection.Up){
			if( (!Physics.Raycast(pos - _halfXVector, Vector3.up,  _rayDist + _yDist / 2, _worldMask))
				&& (Physics.Raycast(pos +_halfXVector, Vector3.up,  _rayDist + _yDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos - _halfXVector;
					_targetPoint = pos - _halfXVector + _halfYVector + _yOffset;
					moveInfo.moveDirection = Vector3.up;
					moveInfo.VerticalDirection = Vector3.left;
					_isClockwise = true;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.up && colDirct == CollisionDirection.Left){
			if( (!Physics.Raycast(pos + _halfYVector, Vector3.left, _rayDist + _xDist / 2, _worldMask))
				&& (Physics.Raycast(pos - _halfYVector, Vector3.left,  _rayDist + _xDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos + _halfYVector;
					_targetPoint = pos - _halfXVector + _halfYVector - _xOffset;
					moveInfo.moveDirection = Vector3.left;
					moveInfo.VerticalDirection = Vector3.up;
					_isClockwise = false;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.down && colDirct == CollisionDirection.Left){
			if( (!Physics.Raycast(pos - _halfYVector, Vector3.left,  _rayDist + _xDist / 2, _worldMask))
				&& (Physics.Raycast(pos +_halfYVector, Vector3.left, _rayDist + _xDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos - _halfYVector;
					_targetPoint = pos - _halfXVector - _halfYVector - _xOffset;
					moveInfo.moveDirection = Vector3.left;
					moveInfo.VerticalDirection = Vector3.down;
					_isClockwise = true;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.up && colDirct == CollisionDirection.Right){
			if( (!Physics.Raycast(pos + _halfYVector, Vector3.right,  _rayDist + _xDist / 2, _worldMask))
				&& (Physics.Raycast(pos - _halfYVector, Vector3.right,  _rayDist + _xDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos + _halfYVector;
					_targetPoint = pos + _halfXVector + _halfYVector + _xOffset;
					moveInfo.moveDirection = Vector3.right;
					moveInfo.VerticalDirection = Vector3.up;
					_isClockwise = true;
					return moveInfo;
			}
		}else if(moveDirct == Vector3.down && colDirct == CollisionDirection.Right){
			if( (!Physics.Raycast(pos - _halfYVector, Vector3.right,  _rayDist + _xDist / 2, _worldMask))
				&& (Physics.Raycast(pos +_halfYVector, Vector3.right,  _rayDist + _xDist / 2, _worldMask))){
					_originPoint = pos;
					_auxiliaryPoint = pos - _halfYVector;
					_targetPoint = pos + _halfXVector - _halfYVector + _xOffset;
					moveInfo.moveDirection = Vector3.right;
					moveInfo.VerticalDirection = Vector3.down;
					_isClockwise = false;
					return moveInfo;
			}
		}

		moveInfo.isTurning = false;
		moveInfo.moveDirection = Vector3.forward;
		moveInfo.VerticalDirection = Vector3.forward;
		return moveInfo;
	}
	
	
	public void TurnAround(float t, float deltaAngle, Transform target){

			Vector3 p1 = Vector3.Lerp(_originPoint, _auxiliaryPoint, t);
			Vector3 p2 = Vector3.Lerp(_auxiliaryPoint, _targetPoint, t);
			target.position = Vector3.Lerp(p1, p2, t);
//			if(target.position != _prePos){
//				Vector3 tempForward = target.position - _prePos;
//				Vector3 tempUpward;
//				if(isClockWise){
//					tempUpward = new Vector3(-tempForward.y, tempForward.x, 0);
//				}else{
//					tempUpward = new Vector3(tempForward.y, -tempForward.x, 0);
//				}
//				target.rotation = Quaternion.LookRotation(tempForward, tempUpward);
//			}
			if(_isClockwise){
				target.Rotate(0, 0, -deltaAngle, Space.Self);
			}else{
				target.Rotate(0, 0, deltaAngle, Space.Self);
			}
			_prePos = target.position;
	}
	
	public CollisionDirection GetCollisionDirection(Vector3 moveDirection, Vector3 position){
		UpdateDetectionPnts(position);
		if(moveDirection == Vector3.right || moveDirection == Vector3.left){
                if(CollidingLeftCondition() == (int)CollisionCondition.FullFacet)
                {
                	return CollisionDirection.Left;
                }
                if(CollidingRightCondition() == (int)CollisionCondition.FullFacet)
                {
                    return CollisionDirection.Right;
                }
                if(CollidingUpCondition() > (int)CollisionCondition.Null)
                {
                    return CollisionDirection.Up;
                }
                if(CollidingDownCondition() > (int)CollisionCondition.Null)
                {
                    return CollisionDirection.Down;
                }
        }
        else if(moveDirection == Vector3.up || moveDirection == Vector3.down){
//				Debug.LogError("moveDirection:" + moveDirection);
//				Debug.DrawRay(position, Vector3.left, Color.red);
                if(CollidingUpCondition() == (int)CollisionCondition.FullFacet)
                {
                    return CollisionDirection.Up;
                }
                if(CollidingDownCondition() == (int)CollisionCondition.FullFacet)
                {
                    return CollisionDirection.Down;
                }
                if(CollidingLeftCondition() > (int)CollisionCondition.Null)
                {
                    return CollisionDirection.Left;
                }
                if(CollidingRightCondition() > (int)CollisionCondition.Null)
                {
                    return CollisionDirection.Right;
                }
        }
        
        return CollisionDirection.None;
	}
	
	private void UpdateDetectionPnts(Vector3 pos){
//		_leftUpPnt = pos - _halfXVector;
//		_leftMidPnt = pos - _halfXVector; 
//		_leftDownPnt = pos - _halfXVector - _halfYVector;
//		_rightUpPnt = pos + _halfXVector + _halfYVector;
//		_rightDownPnt = pos +_halfXVector - _halfYVector;
//		_rightMidPnt = pos + _halfXVector;
//		_upMidPnt = pos + _halfYVector;
//		_downMidPnt = pos - _halfYVector;
		_leftPnt = pos - _halfXVector;
		_rightPnt = pos + _halfXVector;
		_upPnt = pos + _halfYVector;
		_downPnt = pos - _halfYVector;
		
		_adjLeftPnt = pos - _halfXVector + Vector3.right * _rayDist;
		_adjRightPnt = pos + _halfXVector + Vector3.left * _rayDist;
		_adjUpPnt = pos + _halfYVector + Vector3.down * _rayDist;
		_adjDownPnt = pos - _halfYVector + Vector3.up * _rayDist;
	}
	
	#region DetectWhetherAtTheCorner
//	private bool IsRightTurningDown(){
//		if(Physics.Raycast(_leftDownPnt, Vector3.down, _rayDist, _worldMask) 
//			&& !Physics.Raycast(_downMidPnt, Vector3.down, _rayDist, _worldMask)
//			&& !Physics.Raycast(_rightDownPnt, Vector3.down, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsLeftTurningDown(){
//		if(Physics.Raycast(_rightDownPnt, Vector3.down, _rayDist, _worldMask)
//			&& !Physics.Raycast(_downMidPnt, Vector3.down, _rayDist, _worldMask)
//			&& !Physics.Raycast(_leftDownPnt, Vector3.down, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsRightTurningUp(){
//		if(Physics.Raycast(_leftUpPnt, Vector3.up, _rayDist, _worldMask)
//			&& !Physics.Raycast(_upMidPnt, Vector3.up, _rayDist, _worldMask)
//			&& !Physics.Raycast(_rightUpPnt, Vector3.up, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsLeftTurningUp(){
//		if(Physics.Raycast(_rightUpPnt, Vector3.up, _rayDist, _worldMask)
//			&& !Physics.Raycast(_upMidPnt, Vector3.up, _rayDist, _worldMask)
//			&& !Physics.Raycast(_leftUpPnt, Vector3.up, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsUpTurningRight(){
//		if(Physics.Raycast(_rightDownPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_rightMidPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_rightUpPnt, Vector3.right, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsDownTurningRight(){
//		if(Physics.Raycast(_rightUpPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_rightMidPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_rightDownPnt, Vector3.right, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsUpTurningLeft(){
//		if(Physics.Raycast(_leftDownPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_leftMidPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_leftUpPnt, Vector3.right, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
//	
//	private bool IsDownTurningLeft(){
//		if(Physics.Raycast(_leftUpPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_leftMidPnt, Vector3.right, _rayDist, _worldMask)
//			&& !Physics.Raycast(_leftDownPnt, Vector3.right, _rayDist, _worldMask)){
//			return true;
//		}
//		return false;
//	}
	
	#endregion
	
	#region DetectWhichDirectionIsColliding
	private int CollidingLeftCondition(){
		int result = 0;
		if(Physics.Raycast(_adjUpPnt, Vector3.left, _rayDist + _xDist / 2, _worldMask))
			result ++;
		if(Physics.Raycast(_adjDownPnt, Vector3.left, _rayDist + _xDist / 2, _worldMask))
			result ++;
		if(IsCollidingRightFullFacet())
			result = 0;
		return result;
	}
	private int CollidingRightCondition(){
		int result = 0;
		if(Physics.Raycast(_adjUpPnt, Vector3.right, _rayDist + _xDist / 2, _worldMask))
			result ++;
		if(Physics.Raycast(_adjDownPnt, Vector3.right, _rayDist + _xDist / 2, _worldMask))
			result ++;
		if(IsCollidingLeftFullFacet())
			result = 0;
		return result;
	}
	private int CollidingUpCondition(){
		int result = 0;
		if(Physics.Raycast(_adjRightPnt, Vector3.up, _rayDist + _yDist / 2, _worldMask))
			result ++;
		if(Physics.Raycast(_adjLeftPnt, Vector3.up, _rayDist + _yDist / 2, _worldMask))
			result ++;
		if(IsCollidingDownFullFacet())
			result = 0;
		return result;
	}
	private int CollidingDownCondition(){
		int result = 0;
		if(Physics.Raycast(_adjRightPnt, Vector3.down, _rayDist + _yDist / 2, _worldMask))
			result ++;
		if(Physics.Raycast(_adjLeftPnt, Vector3.down, _rayDist + _yDist / 2, _worldMask))
			result ++;
		if(IsCollidingUpFullFacet())
			result = 0;
		return result;
	}
	
	private bool IsCollidingLeftFullFacet(){
		if(Physics.Raycast(_upPnt, Vector3.left, _rayDist + _xDist / 2, _worldMask)
			&&Physics.Raycast(_downPnt, Vector3.left, _rayDist + _xDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingRightFullFacet(){
		if(Physics.Raycast(_upPnt, Vector3.right, _rayDist + _xDist / 2, _worldMask)
		&&Physics.Raycast(_downPnt, Vector3.right, _rayDist + _xDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingUpFullFacet(){
		if(Physics.Raycast(_rightPnt, Vector3.up, _rayDist + _yDist / 2, _worldMask)
		&& Physics.Raycast(_leftPnt, Vector3.up, _rayDist + _yDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingDownFullFacet(){
		if(Physics.Raycast(_rightPnt, Vector3.down, _rayDist + _yDist / 2, _worldMask)
		&& Physics.Raycast(_leftPnt, Vector3.down, _rayDist + _yDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingLeft(){
		if(Physics.Raycast(_upPnt, Vector3.left, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_downPnt, Vector3.left, _rayDist + _xDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingRight(){
		if(Physics.Raycast(_upPnt, Vector3.right, _rayDist + _xDist / 2, _worldMask)
		||Physics.Raycast(_downPnt, Vector3.right, _rayDist + _xDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingUp(){
		if(Physics.Raycast(_rightPnt, Vector3.up, _rayDist + _yDist / 2, _worldMask)
		|| Physics.Raycast(_leftPnt, Vector3.up, _rayDist + _yDist / 2, _worldMask))
			return true;
		return false;
	}
	private bool IsCollidingDown(){
		if(Physics.Raycast(_rightPnt, Vector3.down, _rayDist + _yDist / 2, _worldMask)
		|| Physics.Raycast(_leftPnt, Vector3.down, _rayDist + _yDist / 2, _worldMask))
			return true;
		return false;
	}
	#endregion
	
  	public string GetCollisionTag(Vector3 pos)
    {
        RaycastHit hit;
		if(Physics.Raycast(_upPnt, Vector3.left, out hit,  _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_downPnt, Vector3.left, out hit, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_rightPnt, Vector3.up, out hit, _rayDist + _yDist / 2, _worldMask)
			|| Physics.Raycast(_leftPnt, Vector3.up, out hit, _rayDist + _yDist / 2, _worldMask)
			|| Physics.Raycast(_upPnt, Vector3.right, out hit, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_downPnt, Vector3.right, out hit, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_rightPnt, Vector3.down, out hit, _rayDist + _yDist / 2, _worldMask)
			|| Physics.Raycast(_leftPnt, Vector3.down, out hit, _rayDist + _yDist / 2, _worldMask)){
                return hit.collider.gameObject.tag;
        }
        return string.Empty;
    }
	
	public GameObject GetCollisionObj(Vector3 pos)
    {
        RaycastHit hit;
		if(Physics.Raycast(_upPnt, Vector3.left, out hit,  _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_downPnt, Vector3.left, out hit, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_rightPnt, Vector3.up, out hit, _rayDist + _yDist / 2, _worldMask)
			|| Physics.Raycast(_leftPnt, Vector3.up, out hit, _rayDist + _yDist / 2, _worldMask)
			|| Physics.Raycast(_upPnt, Vector3.right, out hit, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_downPnt, Vector3.right, out hit, _rayDist + _xDist / 2, _worldMask)
			|| Physics.Raycast(_rightPnt, Vector3.down, out hit, _rayDist + _yDist / 2, _worldMask)
			|| Physics.Raycast(_leftPnt, Vector3.down, out hit, _rayDist + _yDist / 2, _worldMask)){
                return hit.collider.gameObject;
        }
        return null;
    }
}
